#include "stdafx.h"

#include "resource.h"
#include "Fire.h"

#include "Registry.h"

#define FPS 25
#define DEFAULT_IDX 6
#define ArraySize(Array) (sizeof(Array)/sizeof(int))

// externals
void BitmapBits(LPCTSTR lpszResource, int *Bits);

// GDI objects
CBitmap hBmp;
CBitmap hMask;
CDC hMemDC1;
CDC hMemDC2;

// Name of registry key
TCHAR szRegistryKey[] = TEXT("Software\\Amazing World\\Computer is the Evil");
// Name of key value
TCHAR szSparks[] = TEXT("Sparks");
TCHAR szFullScreen[] = TEXT("FullScreen");

// Number of sparks at each turn
int nSparks[] = {125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400, 425, 450, 475, 500, 550, 600, 650, 700};
int nSparksIdx = DEFAULT_IDX;

BOOL bWorking = FALSE;
UINT_PTR uTimer = 0;
bool bFullScreen = false;

CFire fire;

int m_nIsThemingSupported = -1;
int m_nIsBufferedPaintSupported = -1;

CRITICAL_SECTION cs = {0};

/**
 * @brief IsVista
 *
 * IsVista
 *
 * @return bool
 */
inline bool IsVista()
{
	OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) };
	BOOL bRet = ::GetVersionEx(&ovi);
	//return false;
	return ((bRet != FALSE) && (ovi.dwMajorVersion >= 6));
}

/**
 * @brief IsBufferedPaintSupported
 *
 * IsBufferedPaintSupported
 *
 * @return bool
 */
bool IsBufferedPaintSupported()
{
	if (m_nIsBufferedPaintSupported == -1)
	{
		if (m_nIsBufferedPaintSupported == -1)
			m_nIsBufferedPaintSupported = IsVista() ? 1 : 0;
	}

	return (m_nIsBufferedPaintSupported == 1);
}

/**
 * @brief IsThemingSupported
 *
 * IsThemingSupported
 *
 * @return bool
 */
bool IsThemingSupported()
{
	if (m_nIsThemingSupported == -1)
	{
		if (m_nIsThemingSupported == -1)
		{
			HMODULE hThemeDLL = ::LoadLibrary(_T("uxtheme.dll"));
			m_nIsThemingSupported = (hThemeDLL != NULL) ? 1 : 0;

			if (hThemeDLL != NULL)
				::FreeLibrary(hThemeDLL);
		}
	}

	return (m_nIsThemingSupported == 1);
}

/**
 * @brief IsAeroSupported
 *
 * IsAeroSupported
 *
 * @return bool
 */
bool IsAeroSupported()
{
	return IsBufferedPaintSupported();
}

/**
 * @brief IsCompositionEnabled
 *
 * IsCompositionEnabled
 *
 * @return bool
 */
bool IsCompositionEnabled()
{
	BOOL bEnabled = FALSE;
	return IsAeroSupported() ? SUCCEEDED(DwmIsCompositionEnabled(&bEnabled)) && bEnabled : false;
}

HTHEME hTheme = NULL;
MARGINS m_Margins = { -1, -1, -1, -1};
//MARGINS m_Margins = {100, 100, 100, 100};

/**
 * @brief ReadIni
 *
 * ReadIni
 *
 * @return void
 */
void ReadIni()
{
	CRegistry Reg;

	if (Reg.Open(HKEY_CURRENT_USER, szRegistryKey))
	{
		nSparksIdx = Reg.GetDWORD(szSparks, DEFAULT_IDX);

		if (nSparksIdx < 0)
			nSparksIdx = 0;

		if (nSparksIdx >= ArraySize(nSparks))
			nSparksIdx = ArraySize(nSparks) - 1;

		bFullScreen = Reg.GetDWORD(szFullScreen, FALSE) != 0;
	}
}

/**
 * @brief WriteIni
 *
 * WriteIni
 *
 * @return void
 */
void WriteIni()
{
	// Save values in registry
	CRegistry Reg;

	if (Reg.Open(HKEY_CURRENT_USER, szRegistryKey))
	{
		Reg.SetDWORD(szSparks, nSparksIdx);
		Reg.SetDWORD(szFullScreen, bFullScreen);
	}
}

/**
 * @brief Init
 *
 * Init
 *
 * @param HWND hWnd
 * @return void
 */
void Init(HWND hWnd)
{
	//
	InitializeCriticalSection(&cs);

	// Get number fs sparks
	ReadIni();

	// Initialize fire
	fire.Init(452, 206);
	fire.SetSparks(nSparks[nSparksIdx]);

	//
	BitmapBits(_T("IDB_FIREMASK"), fire.GetFireMask());
	BitmapBits(_T("IDB_BMPMASK"), fire.GetImageMask());

	CWindowDC hScrDC(hWnd);

	hMemDC2.CreateCompatibleDC(hScrDC);
	hBmp = CreateCompatibleBitmap(hScrDC, 452, 206);
	hMemDC2.SelectBitmap(hBmp);
}

BP_BUFFERFORMAT m_dwFormat = BPBF_TOPDOWNDIB;
BP_PAINTPARAMS m_PaintParams = {0};

/**
 * @brief OnEraseBackground
 *
 * OnEraseBackground
 *
 * @param HWND hWnd
 * @return void
 */
LRESULT OnEraseBackground( HWND hWnd )
{
	EnterCriticalSection(&cs);

	CWindowDC hDC(hWnd);

	CRect rcClient;
	GetClientRect(hWnd, &rcClient);

	if (hTheme != NULL && IsCompositionEnabled())
	{
		DWM_BLURBEHIND bb = {0};
		bb.dwFlags = DWM_BB_ENABLE;
		bb.fEnable = TRUE;
		DwmEnableBlurBehindWindow(hWnd, &bb);

		CDC dcPaint;
		HPAINTBUFFER m_hPaintBuffer = ::BeginBufferedPaint(hDC, &rcClient, m_dwFormat, &m_PaintParams, &dcPaint.m_hDC);

		dcPaint.FillSolidRect(rcClient, RGB(0, 0, 0));

		::EndBufferedPaint(m_hPaintBuffer, TRUE);
	}
	else
	{
		//HBRUSH brush = ::CreateSolidBrush(RGB(0, 0, 0));
		//FillRect (hDC, &rcClient, brush);

		//DeleteObject(brush);
	}

	LeaveCriticalSection(&cs);

	return 1;
}


/**
 * @brief OnCreate
 *
 * OnCreate
 *
 * @param HWND hWnd
 * @param UINT_PTR uTimer
 * @return void
 */
LRESULT OnCreate( HWND hWnd, CREATESTRUCT* pcs )
{
	// Retrieve the application name from the .rc file.
	LoadString(hMainInstance, IDS_APP_NAME, szAppName, 40);

	if (IsThemingSupported())
	{
		hTheme = OpenThemeData(hWnd, L"window");
	}

	m_PaintParams.cbSize = sizeof(BP_PAINTPARAMS);

	// Initialize application
	Init(hWnd);

	// Set timer
	uTimer = SetTimer(hWnd, 1, 1000 / FPS, NULL);

	return 0;
}

/**
 * @brief OnTimer
 *
 * OnTimer
 *
 * @param HWND hWnd
 * @return void
 */
LRESULT OnTimer( HWND hWnd )
{
	if (fChildPreview || bFullScreen)
	{

	}
	else if (!bWorking)
	{
		EnterCriticalSection(&cs);

		CWindowDC hdc(hWnd);

		CRect rc;
		GetClientRect (hWnd, &rc);

		fire.Draw(hdc, (rc.right - rc.left - 452) / 2, (rc.bottom - rc.top - 206) / 2);

		LeaveCriticalSection(&cs);
	}

	return 0;
}

/**
 * @brief OnDestroy
 *
 * OnDestroy
 *
 * @param HWND hWnd
 * @return void
 */
LRESULT OnDestroy( HWND hWnd )
{
	DeleteObject(hMask);
	DeleteObject(hBmp);

	DeleteObject(hMemDC2);
	DeleteObject(hMemDC1);

	if (uTimer)
		KillTimer(hWnd, uTimer);

	return 0;
}

/**
* @brief ScreenSaverProc
*
* ScreenSaverProc
*
* @param HWND hWnd
* @param UINT message
* @param WPARAM wParam
* @param LPARAM lParam
* @return LRESULT WINAPI
*/
LRESULT WINAPI ScreenSaverProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{

	case WM_CREATE:
		OnCreate(hWnd, (CREATESTRUCT*)lParam);
		break;

	case WM_ERASEBKGND:
		return OnEraseBackground(hWnd);

	case WM_TIMER:
		return OnTimer(hWnd);
		break;

	case WM_DESTROY:
		OnDestroy(hWnd);
		break;
	}

	return DefScreenSaverProc(hWnd, message, wParam, lParam);
}

/**
 * @brief DialogOnCommand
 *
 * DialogOnCommand
 *
 * @param WPARAM wParam
 * @param LPARAM lParam
 * @param HWND hDlg
 * @return void
 */
void DialogOnCommand( WPARAM wParam, LPARAM lParam, HWND hDlg )
{
	switch ( LOWORD(wParam) )
	{

	case IDOK:
		nSparksIdx = (int)SendDlgItemMessage(hDlg, IDC_SLIDER1, TBM_GETPOS, 0, 0);
		bFullScreen = SendDlgItemMessage(hDlg, IDC_FULLSCREEN, BM_GETCHECK, 0, 0) != 0;
		WriteIni();
		EndDialog( hDlg, TRUE);
		break;

	case IDCANCEL:
		EndDialog( hDlg, TRUE);
		break;
	}
}

/**
 * @brief DialogOnInit
 *
 * DialogOnInit
 *
 * @param HWND hDlg
 * @return void
 */
void DialogOnInit( HWND hWnd )
{
	InitCommonControls();
	// Read curent config
	ReadIni();
	// Set range of slider
	SendDlgItemMessage(hWnd, IDC_SLIDER1, TBM_SETRANGE, TRUE, MAKELONG(0, ArraySize(nSparks) - 1));
	// Set position of slider
	SendDlgItemMessage(hWnd, IDC_SLIDER1, TBM_SETPOS, TRUE, nSparksIdx);
	// Full screen mode
	SendDlgItemMessage(hWnd, IDC_FULLSCREEN, BM_SETCHECK, (WPARAM)bFullScreen, 0);
}

/**
* @brief ScreenSaverConfigureDialog
*
* ScreenSaverConfigureDialog
*
* @param HWND hDlg
* @param UINT message
* @param WPARAM wParam
* @param LPARAM lParam
* @return BOOL WINAPI
*/
BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{

	case WM_INITDIALOG:
		DialogOnInit(hDlg);
		return TRUE;

	case WM_COMMAND:
		DialogOnCommand(wParam, lParam, hDlg);
		return TRUE;
	}

	return FALSE;
}

/**
* @brief RegisterDialogClasses
*
* RegisterDialogClasses
*
* @param HANDLE hInst
* @return BOOL WINAPI
*/
BOOL WINAPI RegisterDialogClasses(HANDLE hInst)
{
	return TRUE;
}

